package com.dingwen.treasure.file.component;

import cn.hutool.core.util.StrUtil;
import com.dingwen.treasure.base.constant.GlobalConstant;
import lombok.Cleanup;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.zip.Zip64Mode;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * 文件下载、预览
 *
 * @author dingwen
 * @date 2022/11/08
 */
@Component
@Slf4j
public class FileComponent {

    /**
     * 获取文件名称
     *
     * @param fileName 文件名称
     * @return {@link String}
     */
    @SneakyThrows(Throwable.class)
    private String getFileName(String fileName) {
        return StrUtil.isNotBlank(fileName) ?
                URLEncoder.encode(fileName, GlobalConstant.UTF8).replaceAll("\\+", "%20") :
                LocalDateTime.now().toString();
    }


    /**
     * 下载
     *
     * @param httpServletResponse http servlet响应
     * @param fileName            文件名称
     * @param fileSuffix          文件后缀
     */
    public void readyDownload(HttpServletResponse httpServletResponse, String fileName, String fileSuffix) {
        // 获取文件名称
        fileName = getFileName(fileName);

        httpServletResponse.setHeader(HttpHeaders.CONTENT_DISPOSITION,
                "attachment;filename*=utf-8''".concat(fileName).concat(fileSuffix));
    }

    /**
     * 下载
     *
     * @param httpServletResponse http servlet响应
     * @param fullFileName        完整文件名称(包含后缀)
     */
    public void readyDownload(HttpServletResponse httpServletResponse, String fullFileName) {

        httpServletResponse.setHeader(HttpHeaders.CONTENT_DISPOSITION,
                "attachment;filename*=utf-8''".concat(fullFileName));
    }



    /**
     * 执行下载
     *
     * @param httpServletResponse http servlet响应
     * @param fileName            文件名称
     * @param fileSuffix          文件后缀
     * @param inputStream         输入流
     */
    public void doDownload(HttpServletResponse httpServletResponse, String fileName, String fileSuffix,
                           InputStream inputStream) {
        readyDownload(httpServletResponse, fileName, fileSuffix);
        doPreview(httpServletResponse, inputStream);

    }

    /**
     * 执行下载
     *
     * @param httpServletResponse http servlet响应
     * @param fullFileName        完整文件名称(包含后缀)
     * @param inputStream         输入流
     */
    public void doDownload(HttpServletResponse httpServletResponse,
                           String fullFileName,
                           InputStream inputStream) {
        readyDownload(httpServletResponse, fullFileName);
        doPreview(httpServletResponse, inputStream);

    }

    /**
     * 预览
     *
     * @param httpServletResponse http servlet响应
     * @param inputStream         输入流
     */
    public void doPreview(HttpServletResponse httpServletResponse,
                          InputStream inputStream) {
        try {

            // 写出文件--输出流
            OutputStream out = httpServletResponse.getOutputStream();
            byte[] buff = new byte[1024];
            int index;
            //执行写出操作
            while ((index = inputStream.read(buff)) != -1) {
                out.write(buff, 0, index);
                out.flush();
            }
        } catch (Exception e) {
            log.error("执行下载异常：{}", e.getMessage());
        }
    }

    /**
     * zip下载压缩
     *
     * @param httpServletResponse    http servlet响应
     * @param fileName               文件名称
     * @param fileSuffix             文件后缀
     * @param byteArrayOutputStreams 待压缩文件流集合(key: 文件名称 value：文件流)
     */
    public void doCompressZipDownload(HttpServletResponse httpServletResponse, String fileName, String fileSuffix,

                                      Map<String, ByteArrayOutputStream> byteArrayOutputStreams) {
        try {
            readyDownload(httpServletResponse, fileName, fileSuffix);
            @Cleanup
            OutputStream outputStream = httpServletResponse.getOutputStream();

            @Cleanup
            ZipArchiveOutputStream zipArchiveOutputStream = new ZipArchiveOutputStream(outputStream);
            compress(byteArrayOutputStreams, zipArchiveOutputStream);
        } catch (Exception e) {
            log.error("zip下载压缩执行异常：{}", e.getMessage());
        }
    }

    /**
     * zip压缩
     *
     * @param fileName               文件名称
     * @param byteArrayOutputStreams 字节数组输出流
     * @return {@link InputStream}
     */
    public InputStream doCompressZip(String fileName, Map<String, ByteArrayOutputStream> byteArrayOutputStreams) {
        try {
            File file = new File(fileName);
            @Cleanup
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            @Cleanup
            ZipArchiveOutputStream zipArchiveOutputStream = new ZipArchiveOutputStream(fileOutputStream);
            compress(byteArrayOutputStreams, zipArchiveOutputStream);
            FileInputStream fileInputStream = new FileInputStream(file);
            boolean delete = file.delete();
            Assert.isTrue(delete,"临时文件删除失败");
            return fileInputStream;
        } catch (IOException e) {
            log.error("文件压缩为Zip发生错误：{}", e.getMessage());
        }
        return null;
    }

    /**
     * 压缩
     *
     * @param byteArrayOutputStreams 字节数组输出流
     * @param zipArchiveOutputStream zip归档文件输出流
     * @throws IOException ioexception
     */
    public void compress(Map<String, ByteArrayOutputStream> byteArrayOutputStreams,
                         ZipArchiveOutputStream zipArchiveOutputStream) throws IOException {
        zipArchiveOutputStream.setUseZip64(Zip64Mode.AsNeeded);
        zipArchiveOutputStream.setEncoding(GlobalConstant.UTF8);
        for (Map.Entry<String, ByteArrayOutputStream> entry : byteArrayOutputStreams.entrySet()) {
            zipArchiveOutputStream.putArchiveEntry(new ZipArchiveEntry(entry.getKey()));
            zipArchiveOutputStream.write(entry.getValue().toByteArray());
        }
        zipArchiveOutputStream.closeArchiveEntry();
    }


}
